# 线程基础

# 线程使用方式

# 实现 Runnable 接口

class RunnableImpl implements Runnable {
    public void run() {
        // ...
    }
}
new Thread(new RunnableImpl()).start();
  • 函数式接口
new Thread(() -> {
    // ...
}).start();

# 实现 Callable 接口

class CallableImpl implements Callable<Integer> {
    public Integer call() {
        return 123;
    }
}
FutureTask<Integer> ft = new FutureTask<>(new CallableImpl());
new Thread(ft).start();
System.out.println(ft.get());

# 继承 Thread 类

同样需要实现 run() 方法,因为 Thread 类实现了 Runable 接口。

class SubThread extends Thread {
    public void run() {
        // ...
    }
}
new SubThread().start();
  • 匿名类
new Thread() {
    public void run() {
        // ...
    }
}.start();

# 实现接口 VS 继承 Thread

  • 实现 Runnable 接口相比继承 Thread 类有如下优势

    • 可以避免由于 Java 的单继承特性而带来的局限
    • 代码能够被多个线程共享
    • 线程池只能放入实现 Runable 或 Callable 的类
  • 实现 Runnable 接口和实现 Callable 接口的区别

    • Runnable 从 1.1 就有,Callable 是 1.5 之后加上去的

    • 实现 Callable 接口的任务线程能返回执行结果

    • Callable 接口的 call() 方法允许抛出异常,而 Runnable 接口的 run() 方法的异常只能在内部消化,不能继续上抛

      一个线程出了异常,应由所在的线程处理,别的线程没有义务或上下文来处理。

    • 加入线程池运行,Runnable 使用 ExecutorService 的 execute 方法,Callable 使用 submit 方法

# 基础线程机制

# Executor

管理多个独立的异步任务的执行,而无需开发者显式地管理线程的生命周期。

  • CachedThreadPool:一个任务创建一个线程。
  • FixedThreadPool:所有任务只能使用固定大小的线程。
  • SingleThreadExecutor:相当于大小为 1 的 FixedThreadPool。
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> {
    // ...
});
executorService.shutdown();

# Daemon

当 JVM 中运行的线程都是守护线程时,JVM 将退出。

Thread thread = new Thread(() -> {
    // ...
});
thread.setDaemon(true);

# 线程状态转换

线程状态转换

# New

新建状态,创建后尚未启动

# Runnable

可运行状态,可能正在运行,也可能正在等待 CPU 时间片。

包含了操作系统线程状态中的 Running 和 Ready。

# Blocking

阻塞状态,等待获取一个排它锁,如果其线程释放了锁就会结束此状态。

# Waiting

无限期等待状态,等待其他线程显式地唤醒,否则不会被分配 CPU 时间片。

进入 退出
Object#wait() Object#notify() / Object#notifyAll()
Thread#join() 被调用的线程执行完毕
LockSupport#park() -

# Timed Waiting

限期等待状态。

进入 退出
Thread#sleep(long millis) 时间结束
Object#wait(long timeout) 时间结束 / Object.notify() / Object.notifyAll()
Thread#join(long millis) 时间结束 / 被调用的线程执行完毕
LockSupport#parkNanos -
LockSupport#parkUntil -

# Terminated

可以是线程结束任务之后自己结束,或者产生了异常而结束。

# 线程阻塞

# 同步阻塞

运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线程放入“锁池”中。

# 等待阻塞

运行的线程执行 wait() 方法,该线程会释放占用的所有资源,JVM 会把该线程放入“等待池”中。

进入这个状态后,无法自动唤醒,必须由其他线程调用 notify()notifyAll() 方法才能将其唤醒。

# 其他阻塞

运行的线程执行 sleep()join() 方法,或者发出了 I/O 请求时,JVM 会把该线程置为阻塞状态。

sleep() 状态超时、join() 等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入就绪状态。

# 线程中断

# interrupt

线程的实例方法 interrupt() 不能立即中断线程,该方法仅告诉线程已经有中断请求,至于是否中断取决于线程内部逻辑。

线程的实例方法 isInterrupted() 可以检测 请求中断标志 为 true 还是 false。

线程的静态方法 interrupted() 也可以检测 请求中断标志,并自动将 请求中断标志 置为 false。

部分可中断的线程方法,会定期执行 isInterrupted() 方法,检测到变化时停止阻塞并抛出 InterruptedException 异常。

在捕获 InterruptedException 异常的时候会自动将 请求中断标志 置为 false。

# stop

已废弃

强制让线程停止有可能使一些清理性的工作得不到完成。

会对锁定的对象进行解锁,导致数据得不到同步处理,出现数据不一致的问题。

# 参考链接